package com.botarena.server.services;

import java.util.HashMap;
import java.util.Map;

import org.python.core.BytecodeLoader;
import org.python.core.PyCode;
import org.python.core.PyDictionary;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.util.PythonInterpreter;

import com.botarena.server.model.Bot;
import com.botarena.server.services.pythonHelpers.BattleResultHolder;
import com.botarena.server.services.pythonHelpers.BotHolder;
import com.google.appengine.api.datastore.Key;
import com.google.apphosting.api.DeadlineExceededException;

public class BattleProcessor {
	
	private static final String BOT_ARENA_GOMOKU_ARBITER =

		"def process():\n" + 
		"\ttry:\n" + 
		"\t\tbot1.set_params(bot1params)\n" +
		"\texcept Exception, e:\n" + 
		"\t\tresult.onSetParamsException(e, 1)\n" + 
		"\t\treturn\n" +
		"\ttry:\n" + 
		"\t\tbot2.set_params(bot2params)\n" +
		"\texcept Exception, e:\n" + 
		"\t\tresult.onSetParamsException(e, 2)\n" + 
		"\t\treturn\n" + 
		"\tdimension = 15\n" + 
		"\ttimelimit = 1\n" + 
		"\ttable = []\n" + 
		"\tfor i in xrange(0, dimension):\n" + 
		"\t\tcolumn = []\n" + 
		"\t\tfor j in xrange(0, dimension):\n" + 
		"\t\t\tcolumn.append(0)\n" + 
		"\t\ttable.append(column)\n" + 
		"\tmoves = 0\n" + 
		"\tmove = -1\n" + 
		"\tcurrent = bot1\n" + 
		"\twhile True:\n" + 
		"\t\tstart = clock()\n" + 
		"\t\ttry:\n" + 
		"\t\t\tmove = current.respond(move)\n" + 
		"\t\texcept Exception, e:\n" + 
		"\t\t\tresult.onException(e)\n" + 
		"\t\t\treturn\n" + 
		"\t\tif clock()-start > timelimit:\n" + 
		"\t\t\tresult.onTimeLimitExceeded()\n" + 
		"\t\t\treturn\n" + 
		"\t\tif move < 0 or move >= dimension*dimension:\n" + 
		"\t\t\tresult.onIllegalMove(move, \"Not a number or not in acceptable range.\")\n" + 
		"\t\t\treturn\n" + 
		"\t\ty = move % dimension\n" + 
		"\t\tx = move / dimension\n" + 
		"\t\tsymbol = 1 if current == bot1 else 2\n" + 
		"\t\tif table[x][y] > 0:\n" + 
		"\t\t\tresult.onIllegalMove(move, \"Field already occupied.\")\n" + 
		"\t\t\treturn\n" + 
		"\t\tresult.onMove(move)\n" + 
		"\t\tmoves = moves+1\n" + 
		"\t\ttable[x][y] = symbol\n" + 
		"\t\trow = 0\n" + 
		"\t\tfor i in xrange(max(0, x-4), min(dimension-1, x+5)):\n" + 
		"\t\t\tif table[i][y] == symbol:\n" + 
		"\t\t\t\trow = row+1\n" + 
		"\t\t\telse:\n" + 
		"\t\t\t\trow = 0;\n" + 
		"\t\t\tif row == 5:\n" + 
		"\t\t\t\tresult.onGameEnded(False)\n" + 
		"\t\t\t\treturn\n" + 
		"\t\trow = 0;\n" + 
		"\t\tfor i in xrange(max(0, y-4), min(dimension-1, y+5)):\n" + 
		"\t\t\tif table[x][i] == symbol:\n" + 
		"\t\t\t\trow = row+1\n" + 
		"\t\t\telse:\n" + 
		"\t\t\t\trow = 0;\n" + 
		"\t\t\tif row == 5:\n" + 
		"\t\t\t\tresult.onGameEnded(False)\n" + 
		"\t\t\t\treturn\n" + 
		"\t\trow = 0;\n" + 
		"\t\tfor i in xrange(-4, 5):\n" + 
		"\t\t\tif x+i>=0 and x+i<dimension and y+i>=0 and y+i<dimension and table[x+i][y+i] == symbol:\n" + 
		"\t\t\t\trow = row+1\n" + 
		"\t\t\telse:\n" + 
		"\t\t\t\trow = 0\n" + 
		"\t\t\tif row == 5:\n" + 
		"\t\t\t\tresult.onGameEnded(False)\n" + 
		"\t\t\t\treturn\n" + 
		"\t\trow = 0;\n" + 
		"\t\tfor i in xrange(-4, 5):\n" + 
		"\t\t\tif x-i>=0 and x-i<dimension and y+i>=0 and y+i<dimension and table[x-i][y+i] == symbol:\n" + 
		"\t\t\t\trow = row+1\n" + 
		"\t\t\telse:\n" + 
		"\t\t\t\trow = 0\n" + 
		"\t\t\tif row == 5:\n" + 
		"\t\t\t\tresult.onGameEnded(False)\n" + 
		"\t\t\t\treturn\n" + 
		"\t\tif moves == dimension*dimension:\n" + 
		"\t\t\tresult.onGameEnded(True)\n" + 
		"\t\t\treturn\n" + 
		"\t\tcurrent = bot2 if current == bot1 else bot1\n" +
		"process()\n";


	public static void performBattleBetween(Key bot1Key, Key bot2Key, Key contestKey) {
		
		BotService botService = new BotService();
		
		Bot bot1 = botService.getByKey(bot1Key);
		Bot bot2 = botService.getByKey(bot2Key);

		if (!bot1.isActive() || !bot1.isContestant() || !bot2.isActive() || !bot2.isContestant()) {
			return;
		}
		
		PyObject bot1object = instantiate(bot1);
		PyObject bot2object = instantiate(bot2);
		
		BattleResultHolder brh = new BattleResultHolder(bot1Key, bot2Key);
		
		if (bot1object == null && bot2object == null) {
			brh.bothBotsUnregistered();
			BattleService.storeBattle(brh, contestKey);
			return;
		}
		
		if (bot1object == null) {
			brh.botUnregistered(bot1Key);
			BattleService.storeBattle(brh, contestKey);
			return;
		}
		
		if (bot2object == null) {
			brh.botUnregistered(bot2Key);
			BattleService.storeBattle(brh, contestKey);
			return;
		}
		
		Map<PyObject, PyObject> globals = new HashMap<PyObject, PyObject>();
		globals.put(new PyString("bot1"), bot1object);
		globals.put(new PyString("bot2"), bot2object);
		globals.put(new PyString("bot1params"), new PyString(bot1.getParameters()));
		globals.put(new PyString("bot2params"), new PyString(bot2.getParameters()));
		globals.put(new PyString("result"), brh);
		PythonInterpreter pi = new PythonInterpreter(new PyDictionary(globals));
		
		PyCode code = pi.compile(BOT_ARENA_GOMOKU_ARBITER);
		
		try {
			pi.exec(code);
		} catch (Exception e) {
			try {
				if (e.getCause() != null) {
					throw e.getCause();
				} else {
					throw e;
				}
			} catch (DeadlineExceededException ee) {
				brh.onTimeLimitExceeded();
			} catch (Throwable pe) {
				brh.onException(pe);
			}
		}
		
		BattleService.storeBattle(brh, contestKey);

	}

	private static PyObject instantiate(Bot bot) {
				
		PyCode code = BytecodeLoader.makeCode("code$py", bot.getBinary().getBytes(), "<script>");
		BotHolder holder = new BotHolder();
		Map<PyObject, PyObject> globals = new HashMap<PyObject, PyObject>();
		globals.put(new PyString("botarena"), holder);
		PythonInterpreter preparator = new PythonInterpreter(new PyDictionary(globals));
		try {
			preparator.exec("import __builtin__");
			preparator.exec("from time import clock");
			preparator.exec("def __restrict__(*args, **kwargs):\n\traise Exception(\"Import of module '%s' is restricted!\" % args[0])");
			preparator.exec("__builtin__.__import__ = __restrict__");
			preparator.exec("__builtin__.clock = clock");
		} catch (Exception e) {
			// Ugly hack. This exception means that some static fields of PythonInterpreter (e.g. builtins) have already been initialized, so we can carry on. 
		}
		try {
			preparator.exec(code);
		} catch(Exception e) {
			return null;
		}
		return holder.getBot();
	}

}
